namespace nDeveloper.VSS.DifferenceEngine
{
    using System;
    using System.Collections;

    public class DiffEngine
    {
        private IDiffList _dest = null;
        private DiffEngineLevel _level = DiffEngineLevel.FastImperfect;
        private ArrayList _matchList = null;
        private IDiffList _source = null;
        private DiffStateList _stateList = null;

        private bool AddChanges(ArrayList report, int curDest, int nextDest, int curSource, int nextSource)
        {
            bool flag = false;
            int num = nextDest - curDest;
            int num2 = nextSource - curSource;
            int length = 0;
            if (num > 0)
            {
                if (num2 > 0)
                {
                    length = Math.Min(num, num2);
                    report.Add(DiffResultSpan.CreateReplace(curDest, curSource, length));
                    if (num > num2)
                    {
                        curDest += length;
                        report.Add(DiffResultSpan.CreateAddDestination(curDest, num - num2));
                    }
                    else if (num2 > num)
                    {
                        curSource += length;
                        report.Add(DiffResultSpan.CreateDeleteSource(curSource, num2 - num));
                    }
                }
                else
                {
                    report.Add(DiffResultSpan.CreateAddDestination(curDest, num));
                }
                return true;
            }
            if (num2 > 0)
            {
                report.Add(DiffResultSpan.CreateDeleteSource(curSource, num2));
                flag = true;
            }
            return flag;
        }

        public ArrayList DiffReport()
        {
            ArrayList report = new ArrayList();
            int length = this._dest.Count();
            int num2 = this._source.Count();
            if (length == 0)
            {
                if (num2 > 0)
                {
                    report.Add(DiffResultSpan.CreateDeleteSource(0, num2));
                }
                return report;
            }
            if (num2 == 0)
            {
                report.Add(DiffResultSpan.CreateAddDestination(0, length));
                return report;
            }
            this._matchList.Sort();
            int curDest = 0;
            int curSource = 0;
            DiffResultSpan span = null;
            foreach (DiffResultSpan span2 in this._matchList)
            {
                if (!this.AddChanges(report, curDest, span2.DestIndex, curSource, span2.SourceIndex) && (span != null))
                {
                    span.AddLength(span2.Length);
                }
                else
                {
                    report.Add(span2);
                }
                curDest = span2.DestIndex + span2.Length;
                curSource = span2.SourceIndex + span2.Length;
                span = span2;
            }
            this.AddChanges(report, curDest, length, curSource, num2);
            return report;
        }

        private void GetLongestSourceMatch(DiffState curItem, int destIndex, int destEnd, int sourceStart, int sourceEnd)
        {
            int num = (destEnd - destIndex) + 1;
            int num2 = 0;
            int length = 0;
            int start = -1;
            int maxLength = 0;
            for (int i = sourceStart; i <= sourceEnd; i++)
            {
                maxLength = Math.Min(num, (sourceEnd - i) + 1);
                if (maxLength <= length)
                {
                    break;
                }
                num2 = this.GetSourceMatchLength(destIndex, i, maxLength);
                if (num2 > length)
                {
                    start = i;
                    length = num2;
                }
                i += length;
            }
            if (start == -1)
            {
                curItem.SetNoMatch();
            }
            else
            {
                curItem.SetMatch(start, length);
            }
        }

        private int GetSourceMatchLength(int destIndex, int sourceIndex, int maxLength)
        {
            int num = 0;
            while (num < maxLength)
            {
                if (this._dest.GetByIndex(destIndex + num).CompareTo(this._source.GetByIndex(sourceIndex + num)) != 0)
                {
                    return num;
                }
                num++;
            }
            return num;
        }

        public double ProcessDiff(IDiffList source, IDiffList destination)
        {
            DateTime now = DateTime.Now;
            this._source = source;
            this._dest = destination;
            this._matchList = new ArrayList();
            int destCount = this._dest.Count();
            int num2 = this._source.Count();
            if ((destCount > 0) && (num2 > 0))
            {
                this._stateList = new DiffStateList(destCount);
                this.ProcessRange(0, destCount - 1, 0, num2 - 1);
            }
            TimeSpan span = (TimeSpan) (DateTime.Now - now);
            return span.TotalSeconds;
        }

        public double ProcessDiff(IDiffList source, IDiffList destination, DiffEngineLevel level)
        {
            this._level = level;
            return this.ProcessDiff(source, destination);
        }

        private void ProcessRange(int destStart, int destEnd, int sourceStart, int sourceEnd)
        {
            int destIndex = -1;
            int length = -1;
            int maxPossibleDestLength = 0;
            DiffState curItem = null;
            DiffState state2 = null;
            for (int i = destStart; i <= destEnd; i++)
            {
                maxPossibleDestLength = (destEnd - i) + 1;
                if (maxPossibleDestLength <= length)
                {
                    break;
                }
                curItem = this._stateList.GetByIndex(i);
                if (!curItem.HasValidLength(sourceStart, sourceEnd, maxPossibleDestLength))
                {
                    this.GetLongestSourceMatch(curItem, i, destEnd, sourceStart, sourceEnd);
                }
                if (curItem.Status == DiffStatus.Matched)
                {
                    switch (this._level)
                    {
                        case DiffEngineLevel.FastImperfect:
                            if (curItem.Length > length)
                            {
                                destIndex = i;
                                length = curItem.Length;
                                state2 = curItem;
                            }
                            i += curItem.Length - 1;
                            break;

                        case DiffEngineLevel.Medium:
                            if (curItem.Length > length)
                            {
                                destIndex = i;
                                length = curItem.Length;
                                state2 = curItem;
                                i += curItem.Length - 1;
                            }
                            break;

                        default:
                            if (curItem.Length > length)
                            {
                                destIndex = i;
                                length = curItem.Length;
                                state2 = curItem;
                            }
                            break;
                    }
                }
            }
            if (destIndex >= 0)
            {
                int startIndex = state2.StartIndex;
                this._matchList.Add(DiffResultSpan.CreateNoChange(destIndex, startIndex, length));
                if ((destStart < destIndex) && (sourceStart < startIndex))
                {
                    this.ProcessRange(destStart, destIndex - 1, sourceStart, startIndex - 1);
                }
                int num6 = destIndex + length;
                int num7 = startIndex + length;
                if ((destEnd > num6) && (sourceEnd > num7))
                {
                    this.ProcessRange(num6, destEnd, num7, sourceEnd);
                }
            }
        }
    }
}

